package canraid.agent;

import java.util.ArrayList;

import canraid.debug.Level;
import canraid.debug.Logger;

import android.os.Handler;
import android.os.Message;

public abstract class Cagent extends Thread implements Iagent {
	private boolean run = false;
	private boolean fired=false;
	private int queueLength=0;
	private byte[] bufferOutput= new byte[queueLength];
	private ArrayList<Handler> otherHandlers=new ArrayList<Handler>();
	private long timeWaitPolling=0;
	private Handler myHandler=null;

	public Cagent(long timeWaitPolling){
		setDaemon(false);
		this.timeWaitPolling = timeWaitPolling;
		myHandler=__createMyHandler();
		getAgentName();
		
		Logger.setLog((Object)this,Level.ASSERT,"Cagent(). Thread("+getAgentName()+")");
	}
	
	//
	// ABSTRACT
	//
	
	public abstract int getAgentName();
	
	public abstract void close();
	
	protected abstract boolean __implementMyHandler(Message msg);
	
	protected abstract boolean __loop();
	
	protected abstract boolean __setup();
	
	
	//
	// IMPLEMENTED
	//
	public void addExternalHandlers(Handler handler){
		if(!this.otherHandlers.contains(handler)){
			this.otherHandlers.add(handler);
		}
	}

	public void pushInQueue(byte[] data){
		Logger.setLog((Object)this,Level.ASSERT,"pushInQueue(). Thread("+getAgentName()+")");
		
		if (data==null)
			return;
		
		if (data.length<1 )
			return;
		
		synchronized(bufferOutput)
		{
			//Impilatura
			byte[] newBuffer=new byte[bufferOutput.length + data.length];
			System.arraycopy(bufferOutput, 0, newBuffer, 0, bufferOutput.length);
			System.arraycopy(data, 0, newBuffer, bufferOutput.length, data.length);
			bufferOutput=newBuffer;
			queueLength=bufferOutput.length;
		}
		
		Logger.setLog((Object)this,Level.INFO, "pushInQueue(). Thread("+getAgentName()+"). Pushed "+data.length+" bytes, total "+ queueLength+" bytes.");
	}

	public void stopPolling() {
		Logger.setLog((Object)this,Level.ASSERT, "stopPolling(). Thread("+getAgentName()+")");
		
		run = false;
	}
	
	public void startPolling() {
		Logger.setLog((Object)this,Level.ASSERT, "startPolling(). Thread("+getAgentName()+")");
		
		this.start();
	}
	
	public void run()
	{
		run=true;
		fired=true;
		Logger.setLog((Object)this,Level.ASSERT, "run(). Thread("+getAgentName()+")");
		Handler[] handlers=getPoolExternalHandlers();
		try{
			boolean internalRun = __setup();
			
			for (int i=0; i<handlers.length && internalRun;i++)
				handlers[i].obtainMessage(Iagent.ACTION_AGENT_STARTED, getAgentName()).sendToTarget();
		
			while(run && internalRun){
				Thread.sleep(timeWaitPolling);
				internalRun = __loop();
			}
		}catch(Exception e){
			Logger.setLog((Object)this,Level.ERROR, "run(). Thread("+getAgentName()+") error("+e.getMessage()+")");
		}finally{			
			run=false;
			handlers=getPoolExternalHandlers();
			for (int i=0; i<handlers.length;i++)
				handlers[i].obtainMessage(Iagent.ACTION_AGENT_STOPPED, getAgentName()).sendToTarget();
			
			close();
			Logger.setLog((Object)this,Level.ASSERT, "run(). Thread("+getAgentName()+") stopped.");
		}
	}

	public int getQueueLength() {	
		return queueLength;
	}
	
	public Handler[] getPoolExternalHandlers() {	
		Handler[] aux= new Handler[otherHandlers.size()];
		for (int i=0; i<aux.length;i++)
			aux[i]=otherHandlers.get(i);
		
		return aux;
	}
	
	public Handler getHandler() {	
		return myHandler;
	}
	
	protected byte[] __getCopyBufferOutput(boolean commit){
		Logger.setLog((Object)this,Level.ASSERT,"__getCopyBufferOutput(). Thread("+getAgentName()+") commit("+commit+")");
		
		byte[] newBuffer=null;
		synchronized(bufferOutput){
			newBuffer=new byte[bufferOutput.length];
			System.arraycopy(bufferOutput, 0, newBuffer, 0, bufferOutput.length);
			if (commit){
				bufferOutput=new byte[0];
				queueLength=0;
			}
		}
		return newBuffer;
	}
	
	protected boolean __commitCopyBufferOutput(int len){
		Logger.setLog((Object)this,Level.ASSERT,"__commitCopyBufferOutput(). Thread("+getAgentName()+")");
		
		if ((bufferOutput.length-len)<0)
			return false;
		
		byte[] newBuffer=null;
		synchronized(bufferOutput){
			newBuffer=new byte[bufferOutput.length-len];
			System.arraycopy(bufferOutput, len, newBuffer, 0, (bufferOutput.length-len));
			bufferOutput=newBuffer;
			queueLength=bufferOutput.length;
		}
		return true;
	}

	public boolean isFired() {
		return fired;
	}

	public boolean isRunning() {
		return run;
	}
	
	private Handler __createMyHandler() {
		Logger.setLog(this,Level.ASSERT,"__createMyHandler().");
		
		return new Handler() {
	        public void handleMessage(Message msg) {
	            switch (msg.what) {
	            case Iagent.REQUEST_AGENT_START:
	            	Logger.setLog(this,Level.INFO,"handleMessage(). REQUEST_AGENT_START");
	            	Integer NameStart= (Integer) msg.obj;
	            	if (NameStart== getAgentName()){
	            		if (!isRunning() && !isFired()){
	            			startPolling();
	            		}
	            	}
	            break;
	            case Iagent.REQUEST_AGENT_STOP:
	            	Logger.setLog(this,Level.INFO,"handleMessage(). REQUEST_AGENT_STOP");
	            	Integer NameStop= (Integer) msg.obj;
	            	if (NameStop== getAgentName()){
	            		stopPolling();
	            	}
	            break;
	            default:
	            	if (!__implementMyHandler(msg))
	            		Logger.setLog(this,Level.INFO,"handleMessage()."+msg.what+": Type not implemented.");
	            break;
	            }
	        }
	    };
	}
	
}
